#include <iostream>
#include <memory>

/*
unique_ptr 是 C++ 11 提供的用于防止内存泄漏的智能指针中的一种实现，独享被管理对象指针所有权的智能指针。
unique_ptr对象包装一个原始指针，并负责其生命周期。
当该对象被销毁时，会在其析构函数中删除关联的原始指针。

unique_ptr具有->和*运算符重载符，因此它可以像普通指针一样使用。

unique_ptr对象始终是关联的原始指针的唯一所有者。我们无法复制unique_ptr对象，它只能移动。
由于每个unique_ptr对象都是原始指针的唯一所有者，因此在其析构函数中它直接删除关联的指针，不需要任何参考计数。

std::unique_ptr<int> ptr1; //创建一个空的unique_ptr<int>对象，因为没有与之关联的原始指针，所以它是空的。

有两种方法可以检查 unique_ptr 对象是否为空或者是否有与之关联的原始指针。
// 方法1
if(!ptr1)
    std::cout<<"ptr1 is empty"<<std::endl;
// 方法2
if(ptr1 == nullptr)
    std::cout<<"ptr1 is empty"<<std::endl;

std::unique_ptr<Task> taskPtr(new Task(22));//要创建非空的 unique_ptr 对象，需要在创建对象时在其构造函数中传递原始指针
std::unique_ptr<Task> taskPtr(new std::unique_ptr<Task>::element_type(23)); //也行
std::unique_ptr<Task> taskPtr2 = new Task(); // 编译错误
std::unique_ptr<Task> taskPtr = std::make_unique<Task>(34); //C++14

Task *p1 = taskPtr.get(); //使用get()·函数获取管理对象的指针。
taskPtr.reset();//释放delete关联的原始指针并使unique_ptr 对象为空。

// 编译错误 : unique_ptr 不能复制
std::unique_ptr<Task> taskPtr3 = taskPtr2; // Compile error

// 编译错误 : unique_ptr 不能复制
taskPtr = taskPtr2; //compile error

// 通过原始指针创建 taskPtr2
std::unique_ptr<Task> taskPtr2(new Task(55));
// 把taskPtr2中关联指针的所有权转移给taskPtr4
std::unique_ptr<Task> taskPtr4 = std::move(taskPtr2);
// 现在taskPtr2关联的指针为空
if(taskPtr2 == nullptr)
    std::cout<<"taskPtr2 is  empty"<<std::endl;

// taskPtr2关联指针的所有权现在转移到了taskPtr4中
if(taskPtr4 != nullptr)
    std::cout<<"taskPtr4 is not empty"<<std::endl;

// 会输出55
std::cout<< taskPtr4->mId << std::endl;

//release()将释放其关联的原始指针的所有权，并返回原始指针。这里是释放所有权，并没有delete原始指针
std::unique_ptr<Task> taskPtr5(new Task(55));
// 不为空
if(taskPtr5 != nullptr)
    std::cout<<"taskPtr5 is not empty"<<std::endl;
// 释放关联指针的所有权
Task * ptr = taskPtr5.release();
// 现在为空
if(taskPtr5 == nullptr)
    std::cout<<"taskPtr5 is empty"<<std::endl;


*/
namespace unique_ptrLearn {

    struct Task {
        int mId;
        Task(int id) :mId(id) {
            std::cout << "Task::Constructor" << std::endl;
        }
        ~Task() {
            std::cout << "Task::Destructor" << std::endl;
        }
    };

    int main()
    {
        // 空对象 unique_ptr
        std::unique_ptr<int> ptr1;

        // 检查 ptr1 是否为空
        if (!ptr1)
            std::cout << "ptr1 is empty" << std::endl;

        // 检查 ptr1 是否为空
        if (ptr1 == nullptr)
            std::cout << "ptr1 is empty" << std::endl;

        // 不能通过赋值初始化unique_ptr
        // std::unique_ptr<Task> taskPtr2 = new Task(); // Compile Error

        // 通过原始指针创建 unique_ptr
        std::unique_ptr<Task> taskPtr(new Task(23));

        // 检查 taskPtr 是否为空
        if (taskPtr != nullptr)
            std::cout << "taskPtr is  not empty" << std::endl;

        // 访问 unique_ptr关联指针的成员
        std::cout << taskPtr->mId << std::endl;

        std::cout << "Reset the taskPtr" << std::endl;
        // 重置 unique_ptr 为空，将删除关联的原始指针
        taskPtr.reset();

        // 检查是否为空 / 检查有没有关联的原始指针
        if (taskPtr == nullptr)
            std::cout << "taskPtr is  empty" << std::endl;

        // 通过原始指针创建 unique_ptr
        std::unique_ptr<Task> taskPtr2(new Task(55));

        if (taskPtr2 != nullptr)
            std::cout << "taskPtr2 is  not empty" << std::endl;

        // unique_ptr 对象不能复制
        //taskPtr = taskPtr2; //compile error
        //std::unique_ptr<Task> taskPtr3 = taskPtr2;

        {
            // 转移所有权（把unique_ptr中的指针转移到另一个unique_ptr中）
            std::unique_ptr<Task> taskPtr4 = std::move(taskPtr2);
            // 转移后为空
            if (taskPtr2 == nullptr)
                std::cout << "taskPtr2 is  empty" << std::endl;
            // 转进来后非空
            if (taskPtr4 != nullptr)
                std::cout << "taskPtr4 is not empty" << std::endl;

            std::cout << taskPtr4->mId << std::endl;

            //taskPtr4 超出下面这个括号的作用于将delete其关联的指针
        }

        std::unique_ptr<Task> taskPtr5(new Task(66));

        if (taskPtr5 != nullptr)
            std::cout << "taskPtr5 is not empty" << std::endl;

        // 释放所有权
        Task* ptr = taskPtr5.release();

        if (taskPtr5 == nullptr)
            std::cout << "taskPtr5 is empty" << std::endl;

        std::cout << ptr->mId << std::endl;

        delete ptr;

        return 0;
    }
}